import { getModelData, generateUuid } from '@/utils/utils';
import { Divider, Modal, notification, Typography } from 'antd';
import type { ModalFuncProps } from 'antd/lib/modal';
import React from 'react';
import type { RequestOptionsInit, ResponseError } from 'umi-request';
import { extend } from 'umi-request';
import URLMAP from './urlMap';
import { dynamicHeader } from './utils';
import cache from '@/utils/cache';
import { getDvaApp } from 'umi';
import _, { get, set} from 'lodash';
import { getMaskHeaderKey, getSecretKey, decryptByAES } from './secret';

const { serviceIdMap, urlMap } = URLMAP;
export interface RequestOptionsInitExtend extends RequestOptionsInit {
  serviceId?: string;
}

notification.config({
  placement: 'bottomRight',
  bottom: 0,
  duration: 2,
  getContainer: () => document.querySelector('.gant-layout' || document.body),
});

export interface Config {
  showSuccess?: boolean;
  successMessage?: SucMessage | string;
  showWraning?: boolean;
  showError?: boolean;
  isStream?: boolean;
  getParams?: boolean;
  duration?: number;
}

export enum MessageTypes {
  create = 'create',
  remove = 'remove',
  modify = 'modify',
  operate = 'operate',
  sort = 'sort',
  save = 'save',
}

export enum SucMessage {
  createMes = '创建成功',
  removeMes = '删除成功',
  modifyMes = '修改成功',
  relateMes = '关联成功',
  unrelateMes = '取消关联成功',
  operateMes = '操作成功',
  sortMes = '排序成功',
  saveMes = '保存成功',
  updateMes = '更新成功',
  releaseMes = '发布成功',
  importMes = '导入成功',
  exportMes = '导出成功',
  addMes = '添加成功',
  increaseMes = '新增成功',
  editMes = '编辑成功',
  moveMes = '移动成功',
  refreshMes = '刷新成功',
  pushMes = '推送成功',
  checkMes = '校验成功',
}

type requestmethod = <T = any>(url: string, RequestOptionsInitExtend: any, config?: Config) => Promise<T>;

type Request = {
  <T = any>(url: string, options: RequestOptionsInitExtend, config?: Config): Promise<T>;
  post: requestmethod;
  get: requestmethod;
  put: requestmethod;
  delete: requestmethod;
};

const { Paragraph } = Typography;

const defaultModalErrorInfo: ModalFuncProps = {
  title: '系统错误',
  maskClosable: true,
  okText: '知道了',
  okButtonProps: { size: 'small' },
  zIndex: 2000,
  centered: true,
  getContainer: () => document.querySelector('.gant-layout' || document.body),
  onOk: (cb) => cb(),
};

const errorMessage = {
  Timeout: '请求超时',
  TypeError: '网络异常',
};

export interface ErrorProp {
  type?: 'normal' | 'system' | 'abnormal';
  msg?: string;
  msgNode?: React.ReactNode;
  traceId?: string | null;
}

export const ErrorContent = ({ traceId, type = 'normal', msg = '', msgNode }: ErrorProp) => {
  if (type == 'normal') {
    return (
      <div style={{ wordBreak: 'break-all' }}>
        <div
          style={{
            maxHeight: 'calc(100vh - 180px)',
            overflowY: 'auto',
          }}
        >
          {msgNode || <span dangerouslySetInnerHTML={{ __html: msg }} />}
        </div>
        <div style={{ marginTop: 30 }}>
          <Divider orientation="left" />
          错误跟踪号:<Paragraph copyable>{traceId}</Paragraph>
        </div>
      </div>
    );
  } else {
    const errorMsg =
      type == 'system' ? <span dangerouslySetInnerHTML={{ __html: msg }} /> : <span>{msg || '未知异常'}</span>;
    return (
      <div style={{ marginTop: '10px' }}>
        <div>
          {'请将错误跟踪号'}
          {'提交给系统管理员以便进一步分析'}
        </div>
        <div>
          <Paragraph copyable>{traceId}</Paragraph>
        </div>
        <div>
          {'错误信息：'}
          {errorMsg}
        </div>
      </div>
    );
  }
};

/**
 * 异常处理程序
 */
export const TraceIdKey = 'x-g-trace-id';
export const StateKey = 'x-g-process-result';
export const errorDruation = null; //错误信息不消失
export const successDruation = 5;
export const warnDruation = 5;

//错误处理
const errorHandler = (error: ResponseError) => {
  const { response, type } = error;
  const TraceId = response && response.headers.get(TraceIdKey);
  const msg = !TraceId ? errorMessage[type] : '';
  // 其他错误
  Modal.error({
    ...defaultModalErrorInfo,
    content: <ErrorContent traceId={TraceId} msg={msg} type="abnormal" />,
  });
  return Promise.reject(error);
};

/**
 * 配置request请求时的默认参数
 */
const timeout = 30 * 60 * 1000;

const extendRequest = extend({
  errorHandler,
  getResponse: true,
  timeout,
  suffix: '.api', // 统一使用api后缀
});

//进入调试模式
const gantdDubug = () => {
  if (cache.debuggingCache.get('debuggingCache')) {
    return { 'X-G-DEBUG': cache.debuggingCache.get('debuggingCache') };
  } else {
    return {};
  }
};

// 请求拦截, 验证登录状态
extendRequest.interceptors.request.use(function requestInterceptor(url, options) {
  const newOption: any = { ...options };
  const isAddPss = url.endsWith('/productStructure/find.api');
  const pssTypeCode = options?.data?.filterInfo?.pssTypeCode;
  newOption.headers = {
    needHtmlEscape: 'false',
    ...gantdDubug(),
    ...options.headers,
    ...dynamicHeader.current,
    ...(isAddPss && !!pssTypeCode ? { pssTypeCode: pssTypeCode } : {}),
  };
  if (!cache.debuggingCache.get('debuggingCache')) {
    delete newOption['X-G-DEBUG'];
  }
  return {
    url,
    options: newOption,
  };
});

//请求前置令牌桶
const request: requestmethod = function request(
  url,
  options,
  {
    showSuccess = false,
    successMessage,
    showWraning = true,
    showError = true,
    getParams = false,
    isStream = false,
    duration = successDruation,
  }: Config = {},
) {
  const X_G_TRACE_ID = generateUuid();
  //传入拼接的url
  let newUrl = url;
  const [_, urlPrefix] = url.split('/');
  newUrl = serviceIdMap[urlPrefix] ? `/${serviceIdMap[urlPrefix]}${url}` : url;
  // _.set(options, 'headers.X-G-TRACE-ID', X_G_TRACE_ID);
  return extendRequest(newUrl, options).then((params) => {
    const { data: serveData, response } = params;
    const { state, message: msg, warnDescription, log, traces } = serveData;
    const TraceId = response.headers.get(TraceIdKey) || X_G_TRACE_ID;
    if (((log && log.length != 0) || (traces && traces.length != 0)) && cache.debuggingCache.get('debuggingCache')) {
      const { dispatch } = getDvaApp()._store;
      const debuggingData = getModelData('global.debuggingData');
      const obj = {
        log,
        api: newUrl,
        traces,
        id: generateUuid(),
      };
      debuggingData.unshift(obj);
      dispatch({
        type: 'global/save',
        payload: {
          debuggingData,
        },
      });
    }
    const DataMaskValue = response.headers.get(getMaskHeaderKey()) || '';
    if (DataMaskValue != '') {
      const secretKey = getSecretKey(DataMaskValue);
      const base64Key = Buffer.from(secretKey, 'utf8'.toString('base64'));
      const decryption = decryptByAES(serveData.data, base64Key, base64Key);
      serveData.data = JSON.parse(decryption);
    }

    if (state === 'success') {
      // 后台返回成功
      if (showSuccess) {
        notification.success({
          message: successMessage || msg || '操作成功',
          duration,
        });
      }
    }
    // // 警告
    if (state === 'warn' && showWraning) {
      notification.info({
        duration: warnDruation,
        message: warnDescription || msg,
        description: (
          <>
            {'错误跟踪号'}:<Paragraph copyable>{TraceId}</Paragraph>
          </>
        ),
      });
    }
    if (state === 'error' && showError) {
      notification.warning({
        duration: errorDruation,
        message: '提示',
        description: <ErrorContent msg={serveData.message} traceId={TraceId} />,
      });
      return Promise.reject(msg);
    }
    if (state === 'sys-error') {
      Modal.error({
        ...defaultModalErrorInfo,
        content: <ErrorContent msg={msg} traceId={TraceId} type="system" />,
      });
      return Promise.reject(msg);
    }
    return getParams ? params : isStream ? response : serveData.data;
  });
};

//兼容写法
['post', 'get', 'put', 'delete'].forEach((method) => {
  request[method] = function namedRequest(url, options, ...behaver) {
    return request(url, { ...options, method }, ...behaver);
  } as requestmethod;
});

export default request as Request;
